<?php

namespace app\service\payment\gateway;

use app\constant\CommonConstant;
use app\constant\ModelConstant;
use app\exception\BusinessException;
use app\exception\PaypalBusinessException;
use app\libs\PaypalSDK\action\PurchasePaypal;
use app\libs\PaypalSDK\builder\PrePurchaseBuilder;
use app\libs\PaypalSDK\builder\PurchaseBuilder;
use app\libs\PaypalSDK\builder\QuickPayBuilder;
use app\libs\PaypalSDK\builder\RepurchasePaypal;
use app\libs\WeChatSDK\Action\PushMessage;
use app\model\Address;
use app\model\Notify;
use app\model\Orders;
use app\model\ShopsPayment;
use app\queue\CreateDraftOrderQueue;
use app\trait\OrderTrait;
use think\facade\Queue;
use think\helper\Arr;
use think\Request;

class PaypalService implements PaymentInterface
{
    use OrderTrait;
    /**
     * @param Orders $order
     * @param Request $request
     * @return array
     * @throws \Exception
     * @desc 创建订单
     */
    public function placeOrder(Orders $order, Request $request): array
    {
        $client = new PurchasePaypal($order->payment);
        $builder = new PurchaseBuilder($order,$request);
        $log = null;
        try {
            $response = $client->purchase($builder);
            $result = $response['result'] ?? [];
            $log = Notify::saveParams($order->id,['request' => $builder->toArray(), 'result' => $result],Notify::TYPE_PLACE_ORDER,$order->pay_method);
            if (empty($result)) throw new \Exception('create order error');
            if (isset($response['error']) && !empty($response['error'])) $this->checkMerchantLimit($response['error'],$order);
            $order_no = Arr::get($result,'id','');
            if (empty($order_no)) throw new \Exception($response->getMessage());
            $approval_urls = array_filter($result['links'], function ($link) {
                return $link['rel'] == 'approve';
            });
            $approval_urls = Arr::first($approval_urls);
            $approval_url = Arr::get($approval_urls,'href', '');
            $transaction_id = '';
            $order->order_no = $order_no;//保存订单号
            $order->save();
            return compact('transaction_id', 'approval_url','order_no');
        }catch (\Exception $e){
            if($log){
                $logParams = $log->params;
                $logParams['error'] = $e->getMessage();
                $log->save();
            }
            tplog("place_order_" . $order->id.' :',['params'=>$builder->toArray(),'error_msg'=>$e->getMessage()]);
            throw $e;
        }
    }

    //快捷支付
    public function quickPay(Request $request,Orders $order): array
    {
        $log = Notify::saveParams($order->id, $request->all(), Notify::TYPE_DRAFT_QUICK_PAY);
        try {
            $builder = new PrePurchaseBuilder($order, $request);
            $paypal = new PurchasePaypal($order->payment);
            $response = $paypal->purchase($builder);
            $result = Arr::get($response, 'result', []);
            Notify::saveParams($order->id, ['params' => $builder->toArray(), "response" => $result], Notify::TYPE_PLACE_ORDER, ShopsPayment::PAY_METHOD_PAYPAL);
            if (empty($result)) throw new BusinessException("create pre paypal order error");
            if (isset($result['error']) && !empty($result['error'])) $this->checkMerchantLimit($result['error'],$order);
            $order->order_no = Arr::get($result, 'id', '');
            $order->save();
            $links = array_filter($result['links'], function ($item) {
                return $item['rel'] == 'approve';
            });
            $approval_urls = Arr::first($links);
            $approval_url = Arr::get($approval_urls, 'href', '');
            return [
                'order_id' => $order->id,
                'approval_url' => $approval_url,
                'paypal_order_id' => Arr::get($result, 'id', '')
            ];

        }catch (\Exception $e){
            $logParams = $log->params;
            $logParams['error'] = $e->getMessage();
            $log->save();
            throw new BusinessException(CommonConstant::PAYMENT_EXCEPTION);
        }
    }



    public function confirmPayment()
    {
        // TODO: Implement confirmPayment() method.
    }


    /**
     * @param Orders $order
     * @param Request $request
     * @return array
     * @throws \Exception
     * @desc 加购
     */
    public function repurchase(Orders $order, Request $request): array
    {
        $client = new PurchasePaypal($order->payment);
        $builder = new RepurchasePaypal($order,$request);
        $log = null;
        try {
            $response = $client->purchase($builder);
            $result = $response['result'] ?? [];
            $requestParams = $builder->toArray();
            $log = Notify::saveParams($order->id,['request' =>$requestParams, 'result' => $result,'type'=>Notify::TYPE_REPURCHASE],Notify::TYPE_REPURCHASE,$order->pay_method);
            if (empty($result)) throw new \Exception('create order error');
            if (isset($response['error']) && !empty($response['error'])) $this->checkMerchantLimit($response['error'],$order);
            $order_no = Arr::get($result,'id','');
            if (empty($order_no)) throw new \Exception($response->getMessage());
            $approval_urls = array_filter($result['links'], function ($link) {
                return $link['rel'] == 'approve';
            });
            //保存订单编号
            $approval_urls = Arr::first($approval_urls);
            $target_url = Arr::get($approval_urls,'href', '');
            $transaction_id  = '';
            $order->order_no = $order_no;
            $order->save();
            return compact('transaction_id', 'target_url','order_no');
        }catch (\Exception $e){
            if($log){
                $logParams = $log->params;
                $logParams['error'] = $e->getMessage();
                $log->save();
            }
            tplog("repurchase_order_" . $order->id.' :',['params'=>$builder->toArray(),'error_msg'=>$e->getMessage()]);
            throw  $e;
        }
    }

    /**
     * @param array $errors
     * @param Orders $order
     * @return false|void
     * @desc 检测商户限制
     */
    protected function checkMerchantLimit(array $errors,Orders $order)
    {
        $isLimit = false;
        $details = $errors['details'] ?? [];
        $paypalErr = $errors['message'] ?? 'paypal create order is err';
        if(empty($details)) return false;
        $paypalErr = [];
        foreach ($details as $detail) {
            if ($detail['issue'] == 'PAYEE_ACCOUNT_RESTRICTED') {
                $isLimit = true;
                $paypalErr[] = $detail['description'];
                break;
            }else{
                $paypalErr[] = $detail['description'];
            }
        }
        $paypalErr = implode(';',$paypalErr);
        if($isLimit){
            $push = new PushMessage();
            $shopifyHost = $order->shop->website ?: $order->shop->host;
            $msg = "店铺ID: " . $order->shop_id . ",shopify账号: " .$shopifyHost . ',报错信息: ' . $paypalErr;
            $push->pushMessage($msg);
        }
    }

    /**
     * @param Orders $order
     * @return void
     * @throws PaypalBusinessException
     * @desc paypal 支付
     */
    public function capture(Orders $order): void
    {
        try {
            if ($order->order_status == Orders::ORDER_STATUS_COMPLETED) throw new \Exception("order has already been captured.");
            //是否重复支付
            $isRepeatPay = Orders::where('order_no', $order->order_no)
                ->where('id', '<>', $order->id)
                ->where('order_status', Orders::ORDER_STATUS_COMPLETED)
                ->findOrEmpty();
            if (!$isRepeatPay->isEmpty()) throw new \Exception("order repeat pay");//订单重复支付
            $paypalApi = new PurchasePaypal($order->payment);
            #2.完成支付
            $paymentSource = [
                'payment_source' => [
                    'token' => [
                        'id' => $order->order_no,
                        'type' => 'BILLING_AGREEMENT'
                    ],
                ],
            ];
            $payRes = $paypalApi->completePurchase($order->order_no, $paymentSource);
            Notify::saveParams($order->id, ['params' => $paymentSource, 'response' => $payRes], Notify::TYPE_CAPTURE_PURCHASE);//获取notify详情
            $payRes = Arr::get($payRes,'result',[]);
            if (isset($payRes['error']) && !empty($payRes['error'])) throw new \Exception('paypal capture: ' . $payRes['error']['message']);
            $payStatus = Arr::get($payRes, 'status', '');
            $purchase_units = $payRes['purchase_units'] ?? [];
            $payment_source = Arr::get($payRes,'payment_source',[]);
            $isGooglePay = false;
            if(array_key_exists('google_pay',$payment_source) || array_key_exists('apple_pay',$payment_source)){
                $isGooglePay = true;
            }
            if ($purchase_units) {
                $purchaseFirst = Arr::first($purchase_units);
                $payments = $purchaseFirst['payments'];
                $capturesFirst = Arr::first($payments['captures']);
                $tradeId = $capturesFirst['id'];
                $seller_protection = Arr::get($capturesFirst, 'seller_protection', []);
                if ($seller_protection) $seller_protection_status = Arr::get($seller_protection, 'status', '-');
                $actualMoney = Arr::get($capturesFirst, 'amount.value', 0);
                // 实际扣款金额
                $order->actual_money = $actualMoney;
                $order->trade_no = $tradeId;
                if($isGooglePay) {
                    $payStatus = Arr::get($capturesFirst,'status','-');
                }
                $order->pay_status = $payStatus;
                $order->save();
            }
            $isCaptureSuccess = $payStatus == 'COMPLETED';
            //完成订单
            if($isCaptureSuccess) {
                // 如果是快捷支付，并且没有草稿订单，则创建草稿订单
                if($order->is_paypal_kj && !$order->draft_id) Queue::push(\app\queue\CreateDraftOrderQueue::class, $order->id, CreateDraftOrderQueue::QUEUE_NAME);
                Orders::completed($order);
            }else{
                throw new \Exception('paypal capture fail,pay status: '.$payStatus);
            }
        } catch (\Exception $e) {
            Orders::saveExceptionMessage($order,$e->getMessage());
            throw new PaypalBusinessException($e->getMessage());
        }

    }

    /**
     * @param Orders $order
     * @return array|\Psr\Http\Message\StreamInterface|string
     * @throws \Exception
     * @desc 获取paypal详情
     */
    public function getPaypalInfo(Orders $order)
    {
        $paypalAPI = new PurchasePaypal($order->payment);
        $response = $paypalAPI->fetchPurchase($order->order_no);
        Notify::saveParams($order->id, $response, Notify::TYPE_PAYPAL_DETAIL, ShopsPayment::PAY_METHOD_PAYPAL);
        return $response;
    }

    /**
     * @param array $response
     * @return array
     * @desc 解析地址
     */
    public  static function parseAddresses(array $response, int $paypal_method = 0): array
    {
        $purchase_units = Arr::get($response, 'purchase_units', []);
        $email = Arr::get($response, 'payer.email_address', '');
        $phone = Arr::get($response, 'payer.phone.phone_number.national_number', '');
        $purchase = Arr::first($purchase_units);
        $shipping = Arr::get($purchase, 'shipping', []);//收货地址
        $fullName = Arr::get($shipping, 'name.full_name', '');
        if ($fullName) {
            $nameList = explode(' ', $fullName);
            $nameList = array_filter($nameList);
            $firstName = Arr::first($nameList);
            $lastName = Arr::last($nameList);
        } else {
            $firstName = Arr::get($response, 'payer.name.given_name', '');
            $lastName = Arr::get($response, 'payer.name.surname', '');
        }
        $addressData = [];
        $shipping_address = $shipping ? Arr::get($shipping, 'address') : Arr::get($response, 'payer.address');
        $formatAddress = function (array $addr) {
            return [
                'country_code' => Arr::get($addr, 'country_code', 'US'),
                'address1' => Arr::get($addr, 'address_line_1', ""),
                'city' => Arr::get($addr, 'admin_area_2', ""),
                'province' => Arr::get($addr, 'admin_area_1', ""),
                'zip' => Arr::get($addr, 'postal_code', ""),
                'country' => Arr::get($addr, 'country_code', ""),
            ];
        };
        if (in_array($paypal_method, [Orders::PAYPAL_METHOD_GOOGLE, Orders::PAYPAL_METHOD_APPLE])) {
            $key = $paypal_method == Orders::PAYPAL_METHOD_GOOGLE ? 'google_pay' : 'apple_pay';
            $google_pay = Arr::get($response, 'payment_source.' . $key, []);
            $username = Arr::get($google_pay, 'name', '');
            $nameList = explode(' ', $username);
            $nameList = array_filter($nameList);
            $google_pay_first_ame = Arr::first($nameList);
            $google_pay_last_name = Arr::last($nameList);
            $billingAddress = $formatAddress(Arr::get($google_pay, 'card.billing_address', []));
            $billingAddress['first_name'] = $google_pay_first_ame;
            $billingAddress['last_name'] = $google_pay_last_name;
            $billingAddress['phone'] = $phone;
            $shippingAddress = $billingAddress;
        } else {
            $shippingAddress = $formatAddress($shipping_address);
            $shippingAddress['first_name'] = $firstName;
            $shippingAddress['last_name'] = $lastName;
            $shippingAddress['phone'] = $phone;
            $billingAddress = $shippingAddress;
        }
        $shippingAddress['type'] = Address::SHIPPING_ADDRESS;
        $billingAddress['type'] = Address::BILLING_ADDRESS;
        $addressData[] = $shippingAddress;
        $addressData[] = $billingAddress;
        $email = in_array($email, ['sb-ddhd2025@business.example.com', 'sb-sijaj15195977@business.example.com']) ? '291203947@qq.com' : $email;
        return [$addressData, $email];
    }

    /**
     * @param array $response
     * @param Request|null $request
     * @return array
     * @desc 账单明细
     */
    public static function parsePaypalBreakdown(array $response): array
    {
        $purchase_units = Arr::get($response, 'purchase_units', []);
        $purchase = Arr::first($purchase_units);
        $total_present = Arr::get($purchase, 'amount.value');
        $breakdown = Arr::get($purchase, 'amount.breakdown');
        $subtotal_present = Arr::get($breakdown, 'item_total.value', '0.00');
        $shipping_price_present = Arr::get($breakdown, 'shipping.value', '0.00');
        $tax_present = Arr::get($breakdown, 'tax_total.value', '0.00');
        return compact('total_present', 'subtotal_present', 'shipping_price_present', 'tax_present');
    }
}